package com.jl.springbean;

import com.jl.JLReflect;
import com.jl.JLTuple;
import com.jl.springbean.annotation.JLProxy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 代理注册主入口
 */
@Configuration
public class JLBeanScanner implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {

    private MetadataReaderFactory metadataReaderFactory;

    private ResourcePatternResolver resourcePatternResolver;

    private ApplicationContext applicationContext;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) {
        // 获取启动类所在包
        List<String> packages = AutoConfigurationPackages.get(applicationContext);
        // 开始扫描包，获取字节码
        List<Class<?>> superclass = JLReflect.ClassReflect.getPackageClass(packages.get(0));
        superclass.addAll(JLReflect.ClassReflect.getPackageClass("com.jl"));
        // 获取所有自定义接口和实现
        List<JLTuple.Tuple2<Class<?>, Class<?>>> list = new ArrayList<>();
        List<JLTuple.Tuple2<Class<?>, JLProxy>> packageClass = JLReflect.ClassReflect.getPackageClass(packages.get(0), JLProxy.class);
        packageClass.addAll(JLReflect.ClassReflect.getPackageClass("com.jl", JLProxy.class));
        for (JLTuple.Tuple2<Class<?>, JLProxy> aClass : packageClass) {
            Class<?> beanClazz = aClass.getV1();
            List<Class<?>> anInterface = JLReflect.ClassReflect.getInterface(beanClazz);
            list.add(new JLTuple.Tuple2<>(beanClazz, anInterface.get(0)));
        }
        if (list.size() == 0) {
            return;
        }
        for (Class<?> beanClazz : superclass) {
            for (JLTuple.Tuple2<Class<?>, Class<?>> tuple2 : list) {
                boolean instanceOf = JLReflect.ClassReflect.isInstanceOf(tuple2.getV2(), beanClazz);
                if (!instanceOf) {
                    continue;
                }
                // BeanDefinition构建器
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
                GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();

                //在这里，我们可以给该对象的属性注入对应的实例。
                //definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
                // 如果构造函数中不止一个值,可以使用这个api,指定是第几个参数
                definition.getConstructorArgumentValues().addIndexedArgumentValue(0, beanClazz);
                definition.getConstructorArgumentValues().addIndexedArgumentValue(1, tuple2.getV1());

                // 定义Bean工程(最终会用上面add的构造函数参数值作为参数调用JLFactoryBean的构造方法)
                definition.setBeanClass(JLFactoryBean.class);
                //这里采用的是byType方式注入，类似的还有byName等
                definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
                String simpleName = beanClazz.getSimpleName();
                // 首字母小写注入容器
                simpleName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
                beanDefinitionRegistry.registerBeanDefinition(simpleName, definition);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }
}
